﻿using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Athena.RouteAddress.Implementations
{
    using Serialization;
    using Options;
    public class FileServiceMultipleRouteManager : BaseServiceMultipleRouteManager
    {
        private const string _publicFileExtendJson = ".athena";
        private const string _publicFileDirectory = "athena_public_route";

        private string _factPublicFileDirectory;
        private readonly ServiceDescriptor _serviceDescriptor;
        private readonly ISerializer<string> _serializer;
        private readonly FileSystemWatcher _fileSystemWatcher;
        private readonly ILogger<FileServiceMultipleRouteManager> _logger;

        private IEnumerable<ServiceMultipleRouteDescriptor> _serviceMultipleRouteDescriptors;

        public FileServiceMultipleRouteManager(ServiceDescriptor serviceDescriptor, ISerializer<string> serializer, IRouteDescriptorChanger routeDescriptorChanger, ILogger<FileServiceMultipleRouteManager> logger, RouteAddressOption routeAddressOption) : base(routeDescriptorChanger)
        {
            _factPublicFileDirectory = string.IsNullOrWhiteSpace(routeAddressOption.FileServiceMultipleRouteDirectory) ? _publicFileDirectory : routeAddressOption.FileServiceMultipleRouteDirectory;
            if (!Path.IsPathRooted(_factPublicFileDirectory))
            {
                _factPublicFileDirectory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, _factPublicFileDirectory);
            }
            _serviceDescriptor = serviceDescriptor;
            _serializer = serializer;
            _logger = logger;
            _fileSystemWatcher = LoadFileSytemWatcher();
        }


        public override Task ClearAsync()
        {
            var servicePath = Path.Combine(_factPublicFileDirectory, BuildFileName(_serviceDescriptor));
            if (File.Exists(servicePath))
            {
                File.Delete(Path.Combine(servicePath));
            }
            return Task.CompletedTask;
        }

        public override async Task<IEnumerable<ServiceMultipleRouteDescriptor>> GetServiceMultipleRouteDescriptorAsync()
        {
            if (_serviceMultipleRouteDescriptors == null || _serviceMultipleRouteDescriptors.Count() == 0)
            {
                _serviceMultipleRouteDescriptors = await GetServiceMultipleRouteDescriptorsFromFile();
            }
            return _serviceMultipleRouteDescriptors;
        }

        public override async Task SetServiceMultipleRouteDescriptorAsync(IEnumerable<ServiceMultipleRouteDescriptor> serviceMultipleRouteDescriptors)
        {
            var servicePath = Path.Combine(_factPublicFileDirectory, BuildFileName(_serviceDescriptor));
            foreach (var item in serviceMultipleRouteDescriptors)
            {
                try
                {
                    using (var fileStream = new FileStream(servicePath, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None))
                    {
                        fileStream.SetLength(0);
                        using (var writer = new StreamWriter(fileStream, Encoding.UTF8))
                        {
                            await writer.WriteAsync(_serializer.Serialize(item));
                        }
                        fileStream.Close();
                        fileStream.Dispose();
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                }
            }
        }

        private FileSystemWatcher LoadFileSytemWatcher()
        {
            if (!Directory.Exists(_factPublicFileDirectory))
            {
                Directory.CreateDirectory(_factPublicFileDirectory);
            }
            var fileSystemWatcher = new FileSystemWatcher(_factPublicFileDirectory);
            fileSystemWatcher.Created += FileSystemWatcherChanged;
            fileSystemWatcher.Changed += FileSystemWatcherChanged;
            fileSystemWatcher.Deleted += FileSystemWatcherChanged;
            fileSystemWatcher.Renamed += FileSystemWatcherChanged;
            fileSystemWatcher.IncludeSubdirectories = false;
            fileSystemWatcher.EnableRaisingEvents = true;
            return fileSystemWatcher;
        }

        private async void FileSystemWatcherChanged(object sender, FileSystemEventArgs e)
        {
            if (_logger.IsEnabled(LogLevel.Information))
            {
                _logger.LogInformation($"文件{e.Name}发生了变更，重新获取路由信息。");
            }
            try
            {
                var analysisFileName = AnalysisFileName(e.Name);
                if (e.ChangeType.Equals(WatcherChangeTypes.Deleted))
                {
                    var serviceMultipleRouteDescriptor = _serviceMultipleRouteDescriptors.FirstOrDefault(x => x.ServiceDescriptor.Id.Equals(analysisFileName[0]) && x.ServiceDescriptor.Node.Equals(analysisFileName[1]));

                    if (serviceMultipleRouteDescriptor != null)
                    {
                        _serviceMultipleRouteDescriptors = _serviceMultipleRouteDescriptors.Except(new ServiceMultipleRouteDescriptor[] { serviceMultipleRouteDescriptor });

                        OnRemoved(new ServiceMultipleRouteEventArgs(new ServiceMultipleRoute()
                        {
                            ServiceDescriptor = serviceMultipleRouteDescriptor.ServiceDescriptor,
                            ServiceRoutes = serviceMultipleRouteDescriptor.RouteDescriptors.Select(routeDescriptor => routeDescriptorChanger.CreateServiceRoute(routeDescriptor))
                        }));
                    }
                }
                else
                {
                    var serviceMultipleRouteDescriptor = (await AnalysisServiceMultipleRouteDescriptorFromFile(e.FullPath));
                    if (serviceMultipleRouteDescriptor != null && serviceMultipleRouteDescriptor.ServiceDescriptor != null && serviceMultipleRouteDescriptor.RouteDescriptors != null)
                    {
                        var changeServiceMultipleRouteDescriptor = _serviceMultipleRouteDescriptors.FirstOrDefault(x => x.ServiceDescriptor.Id.Equals(x.ServiceDescriptor.Id) && x.ServiceDescriptor.Node.Equals(x.ServiceDescriptor.Node));

                        var serviceMultipleRoute = new ServiceMultipleRoute()
                        {
                            ServiceDescriptor = serviceMultipleRouteDescriptor.ServiceDescriptor,
                            ServiceRoutes = serviceMultipleRouteDescriptor.RouteDescriptors.Select(routeDescriptor => routeDescriptorChanger.CreateServiceRoute(routeDescriptor))
                        };

                        if (changeServiceMultipleRouteDescriptor == null)
                        {
                            _serviceMultipleRouteDescriptors = _serviceMultipleRouteDescriptors.Concat(new ServiceMultipleRouteDescriptor[] { serviceMultipleRouteDescriptor });
                            OnCreated(new ServiceMultipleRouteEventArgs(serviceMultipleRoute));
                        }
                        else
                        {
                            var changeServiceMultipleRoute = new ServiceMultipleRoute()
                            {
                                ServiceDescriptor = changeServiceMultipleRouteDescriptor.ServiceDescriptor,
                                ServiceRoutes = changeServiceMultipleRouteDescriptor.RouteDescriptors.Select(routeDescriptor => routeDescriptorChanger.CreateServiceRoute(routeDescriptor))
                            };
                            _serviceMultipleRouteDescriptors = _serviceMultipleRouteDescriptors.Except(new ServiceMultipleRouteDescriptor[] { changeServiceMultipleRouteDescriptor }).Concat(new ServiceMultipleRouteDescriptor[] { serviceMultipleRouteDescriptor });
                            OnChanged(new ServiceMultipleRouteChangedEventArgs(serviceMultipleRoute, changeServiceMultipleRoute));
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                _logger.LogError($"文件变更失败,{ex.Message}");
            }
        }


        private async Task<IEnumerable<ServiceMultipleRouteDescriptor>> GetServiceMultipleRouteDescriptorsFromFile()
        {
            var files = Directory.GetFiles(_factPublicFileDirectory, $"*{_publicFileExtendJson}");
            var serviceMultipleRouteDescriptors = new List<ServiceMultipleRouteDescriptor>();
            foreach (var item in files)
            {
                var serviceMultipleRoute = await AnalysisServiceMultipleRouteDescriptorFromFile(item);
                if (serviceMultipleRoute != null)
                {
                    serviceMultipleRouteDescriptors.Add(serviceMultipleRoute);
                }
            }
            return serviceMultipleRouteDescriptors;
        }

        private async Task<ServiceMultipleRouteDescriptor> AnalysisServiceMultipleRouteDescriptorFromFile(string file)
        {
            string content = string.Empty;
            ServiceMultipleRouteDescriptor serviceMultipleRouteDescriptor = null;
            if (File.Exists(file))
            {
                if (_logger.IsEnabled(LogLevel.Debug))
                    _logger.LogDebug($"准备从文件：{file}中获取服务路由。");
                try
                {
                    using (var fileStream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
                    {
                        var reader = new StreamReader(fileStream, Encoding.UTF8);
                        content = await reader.ReadToEndAsync();
                        fileStream.Close();
                        fileStream.Dispose();
                    }
                }
                catch (IOException ioException)
                {
                    content = string.Empty;
                    if (_logger.IsEnabled(LogLevel.Error))
                    {
                        _logger.LogError(
                            $"成功获取路由信息失败：{ioException.Message}。");
                    }
                }
                try
                {
                    if (!string.IsNullOrWhiteSpace(content))
                    {
                        serviceMultipleRouteDescriptor = (ServiceMultipleRouteDescriptor)_serializer.Deserialize(content, typeof(ServiceMultipleRouteDescriptor));

                        if (_logger.IsEnabled(LogLevel.Information))
                            _logger.LogInformation(
                                $"成功获取到以下路由信息：{string.Join(",", serviceMultipleRouteDescriptor.RouteDescriptors.Select(x => x.CorrelationId))}。");
                    }
                }
                catch (Exception exception)
                {
                    if (_logger.IsEnabled(LogLevel.Error))
                        _logger.LogError("获取路由信息时发生了错误。", exception);
                }
            }
            else
            {
                if (_logger.IsEnabled(LogLevel.Warning))
                    _logger.LogWarning($"无法获取路由信息，因为文件：{file}不存在。");
            }
            return serviceMultipleRouteDescriptor;
        }


        private string BuildFileName(ServiceDescriptor serviceDescriptor)
        {
            return $"{serviceDescriptor.Id}_{serviceDescriptor.Node}{_publicFileExtendJson}";
        }

        private string[] AnalysisFileName(string name)
        {
            return name.Split('.')[0].Split('_');
        }
    }
}
